package com.example.dayu521.sg_oss.controller;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.example.dayu521.sg_oss.domain.OssIndex;
import com.example.dayu521.sg_oss.service.Oss;
import com.example.dayu521.sg_oss.service.OssIndexService;
import com.example.dayu521.sg_oss.service.impl.DressOss;
import com.example.dayu521.sg_oss.service.impl.RecordOssImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.example.dayu521.OrRes;
import org.example.dayu521.exception.BadObjSize;
import org.example.dayu521.exception.DuplicateOssObj;
import org.example.dayu521.exception.NoSuchOss;
import org.example.dayu521.oss.Mq;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.core.io.InputStreamResource;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * minio 文档https://min.io/docs/minio/linux/developers/java/API.html
 */
@RestController
@RequestMapping("/oss")
@Slf4j
public class OssApi {

    @Resource
    private Oss oss;

    @Resource
    private OssIndexService ossIndexService;

    @Resource
    private RabbitTemplate rabbitTemplate;

    //请求oss服务的上传地址
    @PostMapping("/url_service/{obj}")
    public OrRes<?> upload_url(@PathVariable("obj") String obj, @RequestBody Map<String, Long> pa) {
        //todo 校验权限
        var file_size = pa.get("size");
        if (file_size == null) {
//            log.debug("");
            OrRes.error("缺少size参数");
        }
        var ossIndex = new OssIndex();
        ossIndex.setObj_name(obj);
        ossIndex.setCreate_date(new Date());
        ossIndex.setStatus(0);
        ossIndex.setObj_url("ppx");

        try {
            ossIndexService.save(ossIndex);
        }catch (DuplicateKeyException e){
            return OrRes.error("obj已存在");
        }

        var pp = new DressOss.AskParam(
                obj, file_size
        );
        var dressOss=new DressOss();
        DressOss.AskResult s = null;
        oss.ask_for_help(dressOss);
        try {
            s = dressOss.ask_obj_handle(pp);
        } catch (BadObjSize e) {
            return OrRes.error(e.getMessage());
        }
        return OrRes.ok(s);
    }

    //上传oss服务后,再次调用此接口记录上传结果
    @PostMapping("/record_upload/{obj_name}")
    public OrRes<?> record_upload(@PathVariable(value = "obj_name",required = true) String obj_name){
        var recordOss=new RecordOssImpl(ossIndexService,rabbitTemplate);
        oss.ask_for_help(recordOss);
        if(recordOss.record(obj_name,"")){
            return OrRes.ok();
        }
        return OrRes.error("记录失败");
    }

    //直接上传
    @PostMapping("/upload")
    public OrRes<?> upload(@RequestPart("file") MultipartFile file) throws Exception {

        var r = new Random();
        var url = Long.toString(new Date().getTime()) + r.nextLong();

        var ossIndex = new OssIndex();
        ossIndex.setObj_name(file.getOriginalFilename());
        ossIndex.setCreate_date(new Date());
        ossIndex.setStatus(0);
        ossIndex.setObj_url(url);

        //下面三个操作都有可能失败
        var error_msg="";
        try {
            ossIndexService.save(ossIndex);
            oss.put_obj(file.getOriginalFilename(), file.getInputStream(), file.getSize());
            var sd = new OssIndex();
            sd.setStatus(1);
            //从status=0转换到status=1
            //更新失败时,status=2或者此记录已经被恢复过程删除了
            if(ossIndexService.update(sd,new UpdateWrapper<OssIndex>()
                    .eq("id",ossIndex.getId())
                    .eq("status",0))){
                return OrRes.ok("");
            }
            //如果失败,就不可能再与恢复过程竞争
            error_msg="一般:上传中断,请稍后再试";
        } catch (Exception e){
            if(e instanceof DuplicateKeyException e1){
                //可能:
                // 1 正在上传
                // 2 已经出错
                // 2 已存在
                //都视为出错
                error_msg="异常:文件已存在";
            }else if(e instanceof DuplicateOssObj e1 ){
                error_msg="异常:数据存在不一致,尝试进行恢复";
            }else{
                throw e;
            }
            log.error(e.getMessage());
        }
        //不需要获取发送结果,因为客户端会重试
        rabbitTemplate.convertAndSend("sg_oss.recover.exchange","obj_duplicate."+oss.oss_identify()
                ,new Mq.Recover(ossIndex.getId(),ossIndex.getObj_name()));
        return OrRes.error(error_msg).info_addtion_to(log::error);
    }

    @GetMapping("/download/{filename}")
    public ResponseEntity<?> download(@PathVariable("filename") String file) throws IOException {
        try {
            return ResponseEntity.ok().headers(a -> {
                a.set("Content-Disposition", """
                        attachment;""");
                a.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            }).body(new InputStreamResource(oss.get_obj(file)));
        } catch (NoSuchOss e) {
            log.error("对象不存在 {}", e.getMessage());
            return ResponseEntity.internalServerError().body("对象不存在");
        }
    }

    @PostMapping("/listobj")
    public OrRes<?> listobj(@RequestBody Map<String, Long> param, @RequestHeader("login_id")String login_id) {
        if (param.isEmpty()) {
            return OrRes.error("参数未携带");
        }
        var page = param.getOrDefault("page", 1l);
        if (page < 1) {
            return OrRes.error("page 不能小于1");
        }
        var size = param.getOrDefault("size", 32l);
        if (page < 0) {
            return OrRes.error("size 不能小于0");
        }
        List<?> res = ossIndexService.list_obj_name((page - 1) * size, size);
        return OrRes.ok(res);
    }

}
